package org.ahmadsoft.ropes.impl;

import org.ahmadsoft.ropes.Rope;

public final class ConcatenationRopeSequentialAccess implements CharSequence {

	private final ConcatenationRope root;
	private final int length;

	/** current working child for fast access * */
	private Rope currentChild;

	/** start and end indexes of current working child, for fast access * */
	private int currentChildStart = Integer.MAX_VALUE, currentChildEnd = -1;

	public ConcatenationRopeSequentialAccess(ConcatenationRope root) {
		this.root = root;
		this.length = root.length();
	}

	public char charAt(final int index) {
		// inline
		int currentChildStart = this.currentChildStart;

		if (index >= currentChildStart && index < currentChildEnd)
			return currentChild.charAt(index - currentChildStart);

		// index not in last child, load the new child
		return loadCurrentRope(index);
	}

	public int length() {
		return length;
	}

	public CharSequence subSequence(int start, int end) {
		// in theory we can just get a subsequence from the root, but its better
		// to make sure this method never succeeds since this class is not for
		// that purpose. This is just a byproduct of the interface.
		throw new UnsupportedOperationException();
	}

	/**
	 * Load the current working leaf node child rope, based on index.
	 */
	private char loadCurrentRope(final int index) {
		ConcatenationRope node = root;
		int nodeStart = 0;

		while (true) {
			Rope left = node.getLeft();

			// keep locally for speed
			int ll = left.length();
			// the end of left child in absolute value (relative to root)
			int absoluteLeftEnd = ll + nodeStart;
			if (index < absoluteLeftEnd) {
				// character in left
				if (left instanceof ConcatenationRope) {
					// leaf in left branch
					node = (ConcatenationRope) left;
					continue;
				} else {
					// left is a leaf, update current node and finish here
					currentChild = left;
					currentChildStart = nodeStart;
					currentChildEnd = absoluteLeftEnd;
					return left.charAt(index - nodeStart);
				}
			} else {
				// character in right
				Rope right = node.getRight();
				if (right instanceof ConcatenationRope) {
					// leaf in right branch
					node = (ConcatenationRope) right;
					nodeStart += ll;
					continue;
				} else {
					// right is a leaf, update current node and finish here
					currentChild = right;
					currentChildStart = absoluteLeftEnd;
					currentChildEnd = absoluteLeftEnd + right.length();
					return right.charAt(index - absoluteLeftEnd);
				}
			}
		}
	}
}
